#include "scene_object.h"
#include <cassert>

namespace ray_tracing
{

	void remove_child(scene_object_t * in, scene_object_t * what)
	{
		if(!in)
			return;

		auto it = std::find(in->children.begin(), in->children.end(), what);
		if(it != in->children.end())
		{
			in->children.erase(it);
		}
	}

	bool lookup_children(scene_object_t * in, scene_object_t * what)
	{
		if(!in)
			return false;

		auto it = std::find(in->children.begin(), in->children.end(), what);
		return it != in->children.end();
	} 

	bool lookup_hierarchy_up(scene_object_t * in, scene_object_t * what)
	{
		auto curr = in->parent;
		while(curr)
		{
			if(curr == what)
				return true;

			curr = curr->parent;
		}

		return false;
	}

	scene_object_t::scene_object_t(scene_object_t* in_parent, material_t* in_material)
		: parent(in_parent)
		, local_SRT()
		, material(in_material)
	{
	}

	scene_object_t::~scene_object_t()
	{
		for(auto child : children)
			delete child;
	}

	void scene_object_t::add_child_safe(scene_object_t* child)
	{
		bool error = this == child || lookup_children(this, child) || parent == child && lookup_hierarchy_up(this, child);

		if(error)
			throw insertion_error();

		add_child_fast(child);
	}

	void scene_object_t::set_new_parent(scene_object_t* new_parent)
	{
		detach_from_parent();
		parent = new_parent;
	}

	void scene_object_t::add_child_fast(scene_object_t* child)
	{
		assert( this != child && lookup_children(this,child) == false
			&& parent != child && lookup_hierarchy_up(this,child) == false );

		set_new_parent( this );
		children.push_back(child);
	}

	void scene_object_t::detach_from_parent()
	{
		remove_child( parent, this );
	}

	transform3f scene_object_t::get_world_matrix() const
	{
		return parent ? parent->get_world_matrix() * local_SRT : local_SRT;
	}

	const scene_nodes_t& scene_object_t::get_children() const
	{
		return children;
	}
}